/**
 * 演示访问静态变量
 * 
 *  一、静态变量特点
    1.生命周期：静态变量的生命周期贯穿整个程序的执行周期。它们在程序启动时初始化，并在程序结束时销毁。
    2.不可变性：默认情况下，静态变量是不可变的。这意味着一旦它们被初始化，就不能再被改变。这种不可变性确保了线程安全，因为在多线程环境中，多个线程可以同时访问静态变量而无需担心数据竞争。
    3.类型安全：Rust 是一个类型安全的语言，静态变量也必须明确指定其类型。
    4.可变性：如果需要使用可变的静态变量，必须使用unsafe块实现。
    
    二、比较静态变量和常量
    1.常量与不可变静态变量的一个微妙的区别是静态变量中的值有一个固定的内存地址。使用这个值总是会访问相同的地址。
    常量则允许在任何被用到的时候复制其数据。
    2.另一个区别在于静态变量可以是可变的。访问和修改可变静态变量都是不安全的
 */

use std::sync::{Mutex, LazyLock};
use std::thread;
use std::io;
use std::io::Write;

//通常静态变量会定义在全局作用域中，并且通常是大写字母开头的常量命名规则。
static mut COUNTER: u32 = 0;
static mut ARC_COUNTER: LazyLock<Mutex<u32>> = LazyLock::new(|| Mutex::new(0));
static mut SIGN: u32 = 0;

//允许指向静态变量的共享引用，不要一直提示了
#[allow(static_mut_refs)]
fn main() {
    //1.0 演示单线程下unsafe访问全局静态变量
    access_static_in_singlethread();
    
    //2.0 演示局部静态变量 -- 在方法/函数，代码块内都可以定义static,只是作用范围不同而已
    define_partitional_static();

    //3.0 演示多线程下对全局变量的访问    
    print!("请输入线程数量:");  //注意print!宏不会刷新输出流,除非有换行符，所以需要手动刷新输出流
    std::io::stdout().flush().unwrap();
    let thread_qty = read_thread_qty(); 

    // 先执行原来的示例
    unsafe {
        change_static_in_parallel(thread_qty);
    }    
    unsafe {
        println!("主线程-ARC_COUNTER现在的值: {}", *ARC_COUNTER.lock().unwrap());
        println!("--- 预备.... 开始!!! ");
        //通知所有子线程开始执行
        SIGN = 1;
        //在主线程中循环打印ARC_COUNTER的值，直到其值达到thread_qty
        while *ARC_COUNTER.lock().unwrap() < thread_qty {
            println!("主线程-ARC_COUNTER现在的值: {}", *ARC_COUNTER.lock().unwrap());
            thread::sleep(std::time::Duration::from_millis(5));
        }
        //打印最终的ARC_COUNTER的值
        println!("主线程-ARC_COUNTER最后的值: {}", *ARC_COUNTER.lock().unwrap());
    }
}

#[allow(static_mut_refs)]
fn access_static_in_singlethread() {
    unsafe {
        println!("COUNTER(开始前): {}", COUNTER);
    }
    let add_qty: u32 = 3;
    add_to_count(add_qty);
    unsafe {
        println!("COUNTER(+{}后): {}", add_qty, COUNTER);
    }
}

fn add_to_count(inc: u32) {
    unsafe {
        COUNTER += inc;
    }
}

/**
 * 演示多线程下对全局变量的访问
 */
#[allow(static_mut_refs)]
unsafe fn change_static_in_parallel(thread_qty: u32) {
    //这里仅仅是一个复制，还是不能修改COUNTER
    //利用懒惰指针+互斥锁
    let mut handles = vec![];
    for _ in 0..thread_qty {
        let handle = thread::spawn(|| {
            //等待主线程通知开始执行子线程任务，否则可能有的很快就跑完了，导致主线程还没开始就结束了
            while SIGN == 0 {
                thread::sleep(std::time::Duration::from_millis(50));
            }
            let mut num = ARC_COUNTER.lock().unwrap();
            *num += 1;
            println!("----子线程{:?}+1得到{}", thread::current().id(), *num);
        });
        handles.push(handle);
    }
    //此处不要join,因为它要等待SIGN=1后才执行，且就算SIGN=1，也会被主线程阻塞
}

fn define_partitional_static() {
    //但如果你愿意，也可以定义一个局部静态变量。
    static HELLO_WORLD: &str = "Hello, world!";
    println!("{}", HELLO_WORLD);
}

fn read_thread_qty() -> u32 {
    let mut gts = String::new();
    io::stdin().read_line(&mut gts).expect("读取失败");
    let gts: u32 = gts.trim().parse().expect("请输入一个数字");
    return gts;
}